package com.globallogic.training.javase.university.schedule;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

/**
 * Represents schedule table with columns and rows.
 * 
 * @author Artem.Zeltinsh
 */
public class ScheduleTable {

	private static final String CELL_FORMAT = "%25s|";

	private ScheduleCell[][] cells;

	private ScheduleCell[] columnCaption;

	private ScheduleCell[] rowCaption;

	private int columns;

	private int rows;

	private int cellHeight;

	private int cellWidth = 25;

	public ScheduleTable(int columns, int rows) {

		this.columns = columns;
		this.rows = rows;

		this.cells = new ScheduleCell[columns][rows];
		for (int i = 0; i < columns; i++) {
			cells[i] = new ScheduleCell[rows];
			for (int j = 0; j < rows; j++) {
				cells[i][j] = new ScheduleCell();
			}
		}

		this.columnCaption = new ScheduleCell[columns];
		for (int i = 0; i < columns; i++) {
			columnCaption[i] = new ScheduleCell();
		}

		this.rowCaption = new ScheduleCell[rows];
		for (int j = 0; j < rows; j++) {
			rowCaption[j] = new ScheduleCell();
		}
	}

	public void appendColumnCaption(int column, String line) {
		checkIndexes(column, 0);
		ScheduleCell cell = columnCaption[column];
		cell.appendLine(line);
	}

	public void appendRowCaption(int row, String line) {
		checkIndexes(0, row);
		ScheduleCell cell = rowCaption[row];
		cell.appendLine(line);
	}

	public void appendLine(int column, int row, String line) {
		checkIndexes(column, row);

		ScheduleCell cell = cells[column][row];
		cell.appendLine(line);
		if (cell.getHeight() > cellHeight) {
			cellHeight = cell.getHeight();
		}
	}

	public String getCell(int column, int row) {
		checkIndexes(column, row);
		return StringUtils.defaultIfBlank(cells[column][row].getLine(0), "");
	}

	private void checkIndexes(int column, int row) {
		if (column >= columns) {
			throw new IndexOutOfBoundsException(
					"Input argument 'column' should be less than " + columns);
		}

		if (row >= rows) {
			throw new IndexOutOfBoundsException(
					"Input argument 'row' should be less than " + rows);
		}
	}

	@Override
	public String toString() {
		StringBuilder result = new StringBuilder();

		for (int k = 0; k < cellHeight; k++) {
			result.append(String.format(CELL_FORMAT, ""));
			for (int i = 0; i < columns; i++) {
				String columnCaptionLine = "";
				if (k < columnCaption[i].getHeight()) {
					columnCaptionLine = columnCaption[i].getLine(k);
				}
				result.append(String.format(CELL_FORMAT, columnCaptionLine));
			}
			result.append("\n");
		}
		result.append(getRowSplitter());

		for (int j = 0; j < rows; j++) {
			for (int k = 0; k < cellHeight; k++) {
				String rowCaptionLine = "";
				if (k < rowCaption[j].getHeight()) {
					rowCaptionLine = rowCaption[j].getLine(k); 
				}
				result.append(String.format(CELL_FORMAT, rowCaptionLine));
				for (int i = 0; i < columns; i++) {
					String cellLine = "";
					if (k < cells[i][j].getHeight()) {
						cellLine = cells[i][j].getLine(k);
					}
					result.append(String.format(CELL_FORMAT, cellLine));
				}
				result.append("\n");
			}
			result.append(getRowSplitter());
		}

		return result.toString();
	}

	private String getRowSplitter() {
		StringBuilder result = new StringBuilder();
		for (int i = 0; i <= columns; i++) {
			result.append(StringUtils.repeat("-", cellWidth)).append("|");
		}
		result.append("\n");
		return result.toString();
	}

	class ScheduleCell {
		List<String> lines = new ArrayList<String>();

		public void appendLine(String line) {
			lines.add(line);
		}

		public String getLine(int index) {
			if (index >= lines.size()) {
				throw new IndexOutOfBoundsException(
						"Input 'index' argument should be less than "
								+ lines.size());
			}
			return lines.get(index);
		}

		int getHeight() {
			return lines.size();
		}
	}
}
